<script lang="ts" setup generic="T = Record<string, unknown>">
import { ElTable } from "element-plus";
import type { TableColumn, TableOption, TableLoadFn } from "@/types/table";
import type { BaseResPagination, PaginationParams } from "@/types/api";
import { useEventSystem } from "@/store";
import { SearchPanel, MenuLeft, RenderColumn } from "./modules";
import { trimParams, simpleUniqueId } from "@/utils";
import defaultStyle from "@/config/defaultConfig";
import { EpModal } from "@/components/EpModal";
import { useMessageBox } from "@/utils/tip/messageBox";
import { deepCopy } from "@/utils/copy";
// 统一配置多语言（写在template中太零散）
import { useI18n } from "vue-i18n";
import { showWarning } from "@/utils/tip/message";

const { t } = useI18n();
const TextConfig = computed(() => ({
  operate: t("comps.table.header.operate"),
  addBtn: t("comps.table.menu.addBtn"),
  editBtn: t("comps.table.menu.editBtn"),
  viewBtn: t("comps.table.menu.viewBtn"),
  delBtn: t("comps.table.menu.deleteBtn"),
  exportBtn: t("comps.table.menu.exportBtn"),
  importBtn: t("comps.table.menu.importBtn"),
  deleteTip: t("comps.table.tip.deleteTip"),
  selectionTip: t("comps.table.tip.selectionTip"),
}));

// interface TableLoadFn<T> {
//   (
//     params: PaginationParams & Record<string, string | number>
//   ): Promise<BaseResPagination<T> | undefined>;
// }

defineOptions({ name: "EpTable", inheritAttrs: false });

const props = withDefaults(
  defineProps<{
    modelValue?: Record<string, unknown> | undefined;
    onLoad?: TableLoadFn<T>;
    beforeOpen?: (done: () => void, type: Custom.OperationType, loading: () => void) => void;
    data?: T[] | undefined; // 这里改为 T[] 而不是 unknown[]
    option: TableOption;
  }>(),
  {
    modelValue: undefined,
    onLoad: (_params: PaginationParams) => Promise.resolve({} as BaseResPagination<T>),
    data: undefined,
    beforeOpen: undefined,
  }
);

// 为了最大程度兼容之前项目，这里大部分事件参照avue-crud 的参数
const emit = defineEmits<{
  "update:modelValue": [params: Record<string, unknown>];
  "row-save": [form: Record<string, unknown>];
  "row-update": [form: Record<string, unknown>];
  "row-del": [row: T];
  "row-batch-del": [];
  // "row-view": [row: T];
  "export-file": [];
  "import-file": [];
  "before-open": [done: () => void, type: Custom.OperationType];
}>();

const initFormStateBackup = ref<Record<string, unknown> | undefined>({});
const dialogRef = ref<InstanceType<typeof EpModal>>();
const elTableRef = ref<InstanceType<typeof ElTable>>();
const modalType = ref<Custom.OperationType>("edit");
const menuContainerRef = ref<HTMLDivElement>();
const loading = ref(false);
const records = ref<T[]>([]);
const pageParams: PaginationParams = reactive({ current: 1, size: 10 });
const searchParams = ref<Record<string, unknown>>({});
const total = ref(0);
const showPagination = ref(true);
const searchColumns = ref<TableColumn[]>([]);
const addColumns = ref<TableColumn[]>([]);
const editColumns = ref<TableColumn[]>([]);
const viewColumns = ref<TableColumn[]>([]);
const slotsName = ref<string[]>([]);
const menuBtnNum = ref(3);
const title = ref("");

const { class: _, style: __, ...attrs } = useAttrs();
const slots = useSlots();

const wrapperTableSlots = ["default", "append", "empty"];
type MenuBtnType = "viewBtn" | "editBtn" | "delBtn" | "addBtn" | "exportBtn" | "importBtn";
const BtnStyleConfig = {
  addBtn: { icon: "mdi:add" },
  delBtn: { icon: "ep:delete", type: "danger", plain: true },
  viewBtn: { icon: "View" },
  editBtn: { icon: "Edit" },
  exportBtn: { icon: "mdi:file-export", plain: true, type: "warning" },
  importBtn: { icon: "mdi:file-import", plain: true, type: "success" },
} as const;

const { confirmDelete, submitConfirm } = useMessageBox();
const { privateSubscribe, privateUnsubscribe } = useEventSystem();
const ID = simpleUniqueId();
provide("channelID", ID);

privateSubscribe(ID, "form:init", initFormState);
privateSubscribe(ID, "form:submit", submitForm);

onUnmounted(() => {
  privateUnsubscribe(ID, "form:init", initFormState);
  privateUnsubscribe(ID, "form:submit", submitForm);
});

function initFormState(form: Record<string, unknown>) {
  // console.log("executing initformState");
  // console.log("---form: ", form, props.modelValue);
  if (props.modelValue)
    for (let [k, v] of Object.entries(props.modelValue)) {
      // console.log(`${k}: ${v}`);
      form[k] = v;
    }
  // console.log("form: ", form);
  emit("update:modelValue", form);
}

function submitForm(form: Record<string, unknown>) {
  emit("update:modelValue", form);
  if ("edit" === modalType.value) {
    submitConfirm()
      .then(() => {
        // console.log("executing submitformstate");
        emit("row-update", form);
      })
      .catch(() => {});
  }
  if ("add" === modalType.value) {
    emit("row-save", form);
  }
}

const loadData = async (showLoading = true) => {
  if (props.data) {
    let start = (pageParams.current - 1) * pageParams.size;
    let end = start + pageParams.size;
    records.value = props.data?.slice(start, end);
    total.value = props.data.length;
  } else {
    try {
      let params = trimParams(searchParams.value);

      if (showLoading) loading.value = true;
      const res = await props.onLoad({ ...pageParams, ...params });
      if (res) {
        // console.log("loadData: ", res);
        records.value = res.records;
        total.value = res.total;
      }
    } catch (e) {
      console.error("el-table loading data failed!", e);
    } finally {
      if (showLoading) loading.value = false;
    }
  }
};

function handleColumns(columns: TableColumn[]) {
  const slots: string[] = [];
  // 遍历一遍拿到所需arr，使用filter代码简洁但是需重复遍历
  const searchArr: TableColumn[] = [];
  const addArr: TableColumn[] = [];
  const editArr: TableColumn[] = [];
  const viewArr: TableColumn[] = [];

  columns.forEach((item) => {
    if (item.children?.length) {
      const {
        slots: s,
        searchArr: c,
        addArr: x,
        editArr: y,
        viewArr: z,
      } = handleColumns(item.children);
      slots.push(...s);
      searchArr.push(...c);
      addArr.push(...x);
      editArr.push(...y);
      viewArr.push(...z);
    } else {
      let prop = item.prop;
      if (item["slot"]) slots.push(prop!);
      if (item["slotHeader"]) slots.push(`${prop}-header`);
      if (item["slotExpand"]) slots.push(`${prop}-expand`);
      if (item["search"]) searchArr.push(item);
      if (item["addDisplay"] ?? true) addArr.push(item);
      if (item["editDisplay"] ?? true) editArr.push(item);
      if (item["viewDisplay"] ?? true) viewArr.push(item);
    }
  });

  return {
    slots: slots,
    searchArr,
    addArr,
    editArr,
    viewArr,
  };
}

const filterTableSlots = computed(() => {
  const res: Record<string, unknown> = {};
  for (let name in slots) {
    if (wrapperTableSlots.includes(name)) res[name] = slots[name];
  }
  return res;
});

const filterColumnSlots = computed(() => {
  const res: Record<string, unknown> = {};
  for (let name in slots) {
    if (slotsName.value.includes(name)) res[name] = slots[name];
  }
  return res;
});

onMounted(async () => {
  function isOrder(i: TableColumn) {
    return i?.order ?? false;
  }
  function isOrderSearch(i: TableColumn) {
    return i?.searchOrder ?? false;
  }
  const compareOrder = toolsfunc.createCompare<TableColumn>("order", { defaultValue: 99 });
  const compareSearchOrder = toolsfunc.createCompare<TableColumn>("searchOrder", {
    defaultValue: 99,
  });
  initFormStateBackup.value = deepCopy(props.modelValue);
  console.log("ep-table---onMounted1", filterColumnSlots.value, slots); //#f5f7fa
  menuBtnNum.value = menuBtns.value.length;
  const { addArr, editArr, searchArr, viewArr, slots: s } = handleColumns(props.option.column);
  slotsName.value = s;
  searchColumns.value = searchArr.some(isOrderSearch)
    ? searchArr.sort(compareSearchOrder)
    : searchArr;
  let judgeOrder = viewArr.some(isOrder);
  console.log("order: ", judgeOrder, searchArr.some(isOrder));
  addColumns.value = judgeOrder ? addArr.sort(compareOrder) : addArr;
  editColumns.value = judgeOrder ? editArr.sort(compareOrder) : editArr;
  viewColumns.value = judgeOrder ? viewArr.sort(compareOrder) : viewArr;
  await loadData();
  countMenuBtn();
});

const maxHeight = computed(
  () => window.innerHeight - (48 + 38 + 8 * 2 + 20 * 2 + 12 + 24 + 50 + 40 + 8 + 24)
);

watch(pageParams, async () => {
  await loadData();
});

const onSearch = async (params: Record<string, unknown>) => {
  searchParams.value = { ...params };
  await loadData();
};

const onCloseDialog = () => {
  if (initFormStateBackup.value) emit("update:modelValue", initFormStateBackup.value); // 恢复父组件传递过来的formState初始值
};

const onClickMenu = async (btnType: MenuBtnType | string, row?: T) => {
  // console.log("----:click menu left: ", btnType, row);
  if (props.beforeOpen && dialogRef.value && ["edit", "add", "detail"])
    props.beforeOpen(
      dialogRef.value?.hideLoading,
      btnType as Custom.OperationType,
      dialogRef.value.showLoading
    );
  let showRow = { ...(row || {}), ...(props.modelValue || {}) };
  switch (btnType) {
    case "viewBtn":
      modalType.value = "detail";
      title.value = TextConfig.value.viewBtn + (props.option.viewTitle ?? "");
      dialogRef.value?.show(showRow as Record<string, unknown>);
      // emit("row-view", row!);
      break;
    case "delBtn":
      if (row) emit("row-del", row);
      else {
        let selectionRows = elTableRef.value?.getSelectionRows();
        if (selectionRows?.length) {
          confirmDelete()
            .then(() => {
              emit("row-batch-del");
            })

            .catch(() => {});
        } else showWarning(TextConfig.value.selectionTip);
      }
      break;
    case "editBtn":
      modalType.value = "edit";
      title.value = TextConfig.value.editBtn + (props.option.editTitle ?? "");
      dialogRef.value?.show(showRow as Record<string, unknown>);
      break;
    case "addBtn":
      modalType.value = "add";
      title.value = TextConfig.value.addBtn + (props.option.addTitle ?? "");
      dialogRef.value?.show(undefined);
      break;
    case "exportBtn":
      emit("export-file");
      break;
    case "importBtn":
      emit("import-file");
      break;
    default:
      break;
  }
};

const menuBtns = computed(() => {
  let btns: MenuBtnType[] = ["viewBtn", "editBtn"];
  return btns
    .filter((btn) => props.option[btn])
    .map((btnType: MenuBtnType) => ({ btnType, text: TextConfig.value[btnType] }));
});

const menuLeftBtns = computed(() => {
  let btns: MenuBtnType[] = ["addBtn", "delBtn", "importBtn", "exportBtn"];
  return btns
    .filter((btn) => props.option[btn])
    .map((btnType: MenuBtnType) => ({
      btnType,
      text: TextConfig.value[btnType],
      ...BtnStyleConfig[btnType],
    }));
});

const modalColumns = computed(() => {
  return "add" === modalType.value
    ? addColumns.value
    : "edit" === modalType.value
      ? editColumns.value
      : viewColumns.value;
});

function countMenuBtn() {
  nextTick(() => {
    if (!menuContainerRef.value) return;
    const buttons = menuContainerRef.value.querySelectorAll(".el-button");
    menuBtnNum.value = buttons.length;
  });
}
</script>
<template>
  <div class="w-full h-full p-5 flex flex-col">
    <EpModal
      v-if="modelValue"
      ref="dialogRef"
      :title="title"
      :width="option.dialogWidth ?? defaultStyle.DialogWidth"
      :columns="modalColumns"
      :modal-type="modalType"
      v-bind="option"
      @close="onCloseDialog"
    ></EpModal>
    <SearchPanel :columns="searchColumns" v-bind="option" @search="onSearch"></SearchPanel>
    <MenuLeft class="mb-2" :buttons="menuLeftBtns" @click-btn="onClickMenu"
      ><template #default> <slot name="menu-left"></slot> </template
    ></MenuLeft>
    <el-table
      ref="elTableRef"
      v-loading="loading"
      :row-key="option.rowKey ?? defaultStyle.RowKey"
      :border="option.border ?? defaultStyle.Border"
      :data="records"
      v-bind="attrs"
      class="w-full flex-1"
      :max-height="maxHeight"
      :stripe="option.stripe ?? defaultStyle.Stripe"
    >
      <el-table-column
        v-if="option.selection"
        type="selection"
        :selectable="option.selectable"
        :fixed="option.selectionFixed ?? defaultStyle.Fixed"
        :width="option.selectionWidth ?? defaultStyle.SelectionWidth"
        :align="defaultStyle.TableAlign"
      ></el-table-column>
      <el-table-column
        v-if="option.index"
        type="index"
        :label="defaultStyle.IndexLabelText"
        :fixed="option.indexFixed ?? defaultStyle.Fixed"
        :width="option.indexWidth ?? defaultStyle.IndexWidth"
        :align="defaultStyle.TableAlign"
      ></el-table-column>
      <RenderColumn :column="option.column">
        <template v-for="(_v1, name) in filterColumnSlots" #[name]="slotData">
          <slot :name="name" v-bind="slotData"></slot>
        </template>
      </RenderColumn>
      <el-table-column
        :align="defaultStyle.TableAlign"
        :fixed="option.menuFixed ?? 'right'"
        :label="option.menuTitle ?? TextConfig.operate"
        :width="option.menuWidth ?? defaultStyle.MenuWidth"
      >
        <template #default="scope">
          <div
            ref="menuContainerRef"
            class="menu-layout grid gap-1 justify-items-center"
            :class="[
              {
                'justify-items-start': 'start' === option.menuAlign,
                'justify-items-end': 'end' === option.menuAlign,
                'grid-cols-1': 1 === menuBtnNum,
                'grid-cols-2': 2 === menuBtnNum,
                'grid-cols-3': menuBtnNum >= 3,
              },
            ]"
          >
            <el-button
              v-for="ele in menuBtns"
              :key="ele.btnType"
              link
              type="primary"
              @click="onClickMenu(ele.btnType, scope?.row)"
              >{{ ele.text }}</el-button
            >
            <el-popconfirm
              :title="TextConfig.deleteTip"
              placement="top-start"
              width="180"
              @confirm="onClickMenu('delBtn', scope?.row)"
            >
              <template #reference>
                <el-button type="danger" link>{{ TextConfig.delBtn }}</el-button>
              </template>
            </el-popconfirm>
            <slot name="menu" v-bind="scope"></slot>
          </div>
        </template>
      </el-table-column>
      <template v-for="(_v, name) in filterTableSlots" #[name]="slotData">
        <slot :name="name" v-bind="slotData"></slot>
      </template>
      <template v-if="!slots['empty']" #empty>
        <el-empty :image-size="100"></el-empty>
      </template>
    </el-table>
    <el-pagination
      v-show="showPagination"
      v-model:current-page="pageParams.current"
      v-model:page-size="pageParams.size"
      size="small"
      class="mt-2 self-end"
      :total="total"
      layout="total, sizes, prev, pager, next, jumper"
      :page-sizes="[2, 5, 10, 20, 30, 50, 100]"
      background
    ></el-pagination>
  </div>
</template>
<style lang="scss" scoped>
.el-table {
  --el-table-header-bg-color: #f5f7fa;
}

.menu-layout {
  :deep(.el-button + .el-button) {
    margin-left: 0;
  }
}
</style>
